iT邦幫忙

2026 iThome 鐵人賽

DAY 14
0
Vibe Coding

使用 Vibe Coding 開發一套 Tutting 模擬器吧!系列 第 14

📐DAY 14 | 修正 FK 切換 IK 姿勢跳動 Bug 🐛

  • 分享至 

  • xImage
  •  

在 DAY 14 我們成功建立了手腳 Two-Bone IK 兩節解析解與「橙色目標球 / 黃綠極向球」雙球控制系統。但在實際測試時,我發現了一個問題:「當我先用 FK 細細調好一個完美的手臂彎曲姿態,一按開關切換到 IK,整條手臂會瞬間地跳動扭轉成另一個奇怪角度!

突然想到 Vibe coding 過程中都只是注重於開發,好像沒有研究如何排除異常!?!?

所以,今天 DAY 15 我們要深入解構這個「姿勢瞬間跳動 Bug」的背後根因吧!

https://ithelp.ithome.com.tw/upload/images/20260908/20144288qoXPjzOjk7.jpg

Vibe coding 異常分析流程

Vibe Coding 遇到異常時的分析流程,重點是兩個排查迴圈:

  • 重現不了 → 先加強紀錄(補 log、加中斷點),再回頭試著重現
  • AI 提出的假設驗證不成立 → 用二分法縮小範圍,再回去讓 AI 重新分析
  • 修復後測試沒過 → 直接回到「實作修復」重新調整

https://ithelp.ithome.com.tw/upload/images/20260828/20144288ESxpa6Dgt3.png

🐛 Bug 現象與根因深度剖析

現象說明

當使用者用 FK 旋轉關節擺好姿態後,開啟 IK 時,理論上目標球與極向球應該「無縫接軌」對齊當前姿勢,但系統卻強制將手肘拉到了另一個彎曲平面。

[ FK 擺好彎曲姿勢 ] → (開啟 IK 開關) → [ 手肘瞬間扭轉跳動!姿態崩壞 ]

AI 根因分析

問題出在開啟 IK 瞬間執行的 syncIKMarkersToDefault() 函式。原本的舊程式碼寫法如下:

JavaScript
// 有問題的舊版本
const mp = new THREE.Vector3();
midBone.getWorldPosition(mp);
ikPoleMeshes[limb].position.copy(mp.clone().add(chain.poleOffset));
  • 目標球(End Effector):定位在手掌世界座標,位置完全正確。
  • 極向球(Pole Target):位置使用了 固定寫死的猜測向量(如 (0, -0.15, 0.35)),這完全忽略了當前 FK 肢體實際彎曲的物理平面!

Two-Bone IK 解算時,手肘向哪一側彎曲,是由極向球相對於 root → target 的側向分量(bendDir)決定。若極向球被放到了固定位置,算出的彎曲平面就會與實際姿勢產生角度偏差,導致求解出的手肘世界方向被瞬間轉走。

向量幾何解法:從 FK 實際姿態反推極向方向

要達成「FK 切換 IK 零跳動」,初始化極向球時就不能用猜的,而是要從目前 FK 骨骼的世界座標反推實際的彎曲向量 bendDir:

步驟 向量計算邏輯 幾何意義
1. 取得關節座標 取得 rootPos(肩)、midPos(肘)、endPos(手)世界座標。 建立三點共面的幾何基礎。
2. 提取軸線方向 targetDir = normalize(endPos - rootPos) 取得肢體延伸的主軸向量。
3. 向量投影扣除 toMid = midPos - rootPoslateral = toMid - targetDir * dot(toMid, targetDir) 算出手肘點相對於主軸的垂直側向向量。
4. 極向向量正規化 lateral.length() > epsilon bendDir = normalize(lateral)否則使用預設 chain.poleOffset 取得當前手臂真正彎曲的方向;打直時則退回猜測值。
5. 極向球定位 polePos = midPos + bendDir * poleDist 將極向球推至手肘側向外側,精準鎖定彎曲平面!

驗證成果與結語

修正完成後,我們可以進行以下情境驗證:

  • 自然彎曲驗證:先用 FK 將手肘往前或往旁彎曲任意角度,切換該肢體 IK 開關,畫面上的手肘與姿態 100% 保持不動,實現完美無縫接軌!
  • 邊界條件驗證:當手臂完全打直(無側向向量可反推)時,系統安全退回預設方向,不會產生 NaN 運算錯誤。

修好這個 Bug 後,FK 與 IK 的雙向流動終於完全順暢!可以用 FK 快速抓大角度,再一鍵切換 IK 用橙/黃綠雙球進行細節調整。


修正「FK 編輯完切換到 IK 時姿勢瞬間變形」的 Prompt

# Prompt:修正「FK 編輯完切換到 IK 時姿勢瞬間變形」的 Bug

## 背景
Tutting 3D 模擬器已經有手腳 IK 功能:每條肢體開啟 IK 時,會有一顆橙色**目標球**(控制
手掌/腳掌想到達的世界座標)和一顆黃綠色八面體**極向球**(控制手肘/膝蓋的彎曲方向)。
開啟 IK 的當下,程式會呼叫 `syncIKMarkersToDefault(limb)` 把這兩顆球定位到「目前姿勢」
對應的預設位置,理論上應該要無縫接軌、不改變畫面上的姿勢。

## 現象(Bug)
使用者先用 FK(點關節球、拖旋轉環)擺好一個手臂彎曲的姿勢,接著切換該肢體的 IK 開關,
畫面上的手臂會**瞬間跳到另一個姿勢**(手掌位置通常還在原地附近,但手肘彎的方向明顯不對,
整條手臂像是被扭轉了一下)。

## 根因
問題出在 `syncIKMarkersToDefault()` 對「極向球」初始位置的計算方式:

``js
// 有問題的版本
const mp = new THREE.Vector3();
midBone.getWorldPosition(mp);
ikPoleMeshes[limb].position.copy(mp.clone().add(chain.poleOffset));
``

`chain.poleOffset` 是一個**固定寫死的猜測向量**(例如手臂是 `(0,-0.15,0.35)`),跟目前
FK 姿勢實際彎曲的方向完全無關。

兩節 IK 求解時,手掌能不能碰到目標點只跟「root→target 距離」有關(這個距離在切換當下是
對的,因為目標球本來就設在目前手掌位置);但「手肘要往哪一側彎」是由極向球的**方向**決定:

``js
const toPole = polePos.clone().sub(rootPos);
const poleOnAxis = targetDir.clone().multiplyScalar(toPole.dot(targetDir));
const bendDir = toPole.clone().sub(poleOnAxis);           // 極向球相對 root→target 連線的側向分量
const bendAxis = crossVectors(targetDir, bendDir).normalize();
const midDir = targetDir.clone().applyAxisAngle(bendAxis, angleRoot);  // 算出新的肘部方向
``

如果極向球方向(固定猜測值)跟目前姿勢的實際彎曲方向不一致,算出來的彎曲平面就會不同,
求解出的肘部方向會跟目前實際方向差一個明顯角度,導致整條手臂在切換 IK 的那一瞬間被轉到
另一個彎曲平面 —— 這就是「瞬間變形」的成因。目標球的位置沒有問題,出錯的只有極向球。

## 修正方法
初始化極向球時,**不要用固定猜測方向,改成從目前 FK 姿勢反推出實際的彎曲方向**:

1. 取得 root / mid / end 三根骨骼目前的世界座標。
2. 算出 `root→end` 方向 `rootToEndDir`(即目前的 targetDir)。
3. 算出 `root→mid` 向量在「垂直於 `rootToEndDir`」方向上的分量 `lateral`
   (做法:`toMid` 減去它投影在 `rootToEndDir` 上的分量)。
4. 若 `lateral` 長度夠大(手臂/腿有明顯彎曲),就把它正規化當作 `bendDir`;
   若太接近 0(手臂/腿完全打直,沒有側向分量可反推,這是唯一無法還原方向的邊界情況),
   才退回使用原本的固定猜測方向 `chain.poleOffset` 正規化後的方向。
5. 極向球最終位置 = `midPos + bendDir * poleDist`,其中 `poleDist` 建議沿用原本
   `chain.poleOffset` 的長度(只換方向、不換距離),維持極向球跟骨骼之間視覺上合理的間距。

修正後效果:因為 `bendDir` 會對齊目前實際姿勢,切換 FK→IK 當下算出的
`deltaRoot`/`deltaMid`(世界空間旋轉增量)應該接近單位旋轉,姿勢不會跳動,只有在使用者
接著拖動目標球或極向球時才會開始明顯改變姿勢。

## 交付要求
- 只修改 `syncIKMarkersToDefault()` 這個函式內部「極向球定位」的邏輯,目標球的定位方式
  不需要改。
- 不要更動兩節 IK 求解函式 `solveTwoBoneIK()` 本身的數學,這個函式已經正確,問題完全出在
  初始化時極向球放的位置不對。
- 修正後要能通過的驗證情境:先用 FK 把手臂彎成任意角度(例如手肘往前彎 90 度),
  切換該肢體 IK 開關,畫面上的手臂姿勢應該維持原樣,不會有明顯跳動;只有在手臂完全打直
  (target 距離 = upperLen + lowerLen)時才允許用預設猜測方向。

上一篇
📐DAY 13 | 兩節解析解 IK(Two-Bone Analytic IK) 與雙球控制系統實作
下一篇
📐DAY 15 | 從街舞藝術到精準數據!3D Tutting 模擬器實務應用的 5 大場景
系列文
使用 Vibe Coding 開發一套 Tutting 模擬器吧!25
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言